/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.pop3; import java.io.IOException; import java.io.Reader; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Enumeration; import java.util.StringTokenizer; import org.apache.commons.net.io.DotTerminatedMessageReader; /*** * The POP3Client class implements the client side of the Internet POP3 * Protocol defined in RFC 1939. All commands are supported, including * the APOP command which requires MD5 encryption. See RFC 1939 for * more details on the POP3 protocol. * <p> * Rather than list it separately for each method, we mention here that * every method communicating with the server and throwing an IOException * can also throw a * {@link org.apache.commons.net.MalformedServerReplyException} * , which is a subclass * of IOException. A MalformedServerReplyException will be thrown when * the reply received from the server deviates enough from the protocol * specification that it cannot be interpreted in a useful manner despite * attempts to be as lenient as possible. * <p> * <p> * @author Daniel F. Savarese * @see POP3MessageInfo * @see org.apache.commons.net.io.DotTerminatedMessageReader * @see org.apache.commons.net.MalformedServerReplyException ***/ public class POP3Client extends POP3 { private static POP3MessageInfo __parseStatus(String line) { int num, size; StringTokenizer tokenizer; tokenizer = new StringTokenizer(line); if (!tokenizer.hasMoreElements()) return null; num = size = 0; try { num = Integer.parseInt(tokenizer.nextToken()); if (!tokenizer.hasMoreElements()) return null; size = Integer.parseInt(tokenizer.nextToken()); } catch (NumberFormatException e) { return null; } return new POP3MessageInfo(num, size); } private static POP3MessageInfo __parseUID(String line) { int num; StringTokenizer tokenizer; tokenizer = new StringTokenizer(line); if (!tokenizer.hasMoreElements()) return null; num = 0; try { num = Integer.parseInt(tokenizer.nextToken()); if (!tokenizer.hasMoreElements()) return null; line = tokenizer.nextToken(); } catch (NumberFormatException e) { return null; } return new POP3MessageInfo(num, line); } /*** * Login to the POP3 server with the given username and password. You * must first connect to the server with * {@link org.apache.commons.net.SocketClient#connect connect } * before attempting to login. A login attempt is only valid if * the client is in the * {@link org.apache.commons.net.pop3.POP3#AUTHORIZATION_STATE AUTHORIZATION_STATE } * . After logging in, the client enters the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } * . * <p> * @param username The account name being logged in to. * @param password The plain text password of the account. * @return True if the login attempt was successful, false if not. * @exception IOException If a network I/O error occurs in the process of * logging in. ***/ public boolean login(String username, String password) throws IOException { if (getState() != AUTHORIZATION_STATE) return false; if (sendCommand(POP3Command.USER, username) != POP3Reply.OK) return false; if (sendCommand(POP3Command.PASS, password) != POP3Reply.OK) return false; setState(TRANSACTION_STATE); return true; } /*** * Login to the POP3 server with the given username and authentication * information. Use this method when connecting to a server requiring * authentication using the APOP command. Because the timestamp * produced in the greeting banner varies from server to server, it is * not possible to consistently extract the information. Therefore, * after connecting to the server, you must call * {@link org.apache.commons.net.pop3.POP3#getReplyString getReplyString } * and parse out the timestamp information yourself. * <p> * You must first connect to the server with * {@link org.apache.commons.net.SocketClient#connect connect } * before attempting to login. A login attempt is only valid if * the client is in the * {@link org.apache.commons.net.pop3.POP3#AUTHORIZATION_STATE AUTHORIZATION_STATE } * . After logging in, the client enters the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } * . After connecting, you must parse out the * server specific information to use as a timestamp, and pass that * information to this method. The secret is a shared secret known * to you and the server. See RFC 1939 for more details regarding * the APOP command. * <p> * @param username The account name being logged in to. * @param timestamp The timestamp string to combine with the secret. * @param secret The shared secret which produces the MD5 digest when * combined with the timestamp. * @return True if the login attempt was successful, false if not. * @exception IOException If a network I/O error occurs in the process of * logging in. * @exception NoSuchAlgorithmException If the MD5 encryption algorithm * cannot be instantiated by the Java runtime system. ***/ public boolean login(String username, String timestamp, String secret) throws IOException, NoSuchAlgorithmException { int i; byte[] digest; StringBuffer buffer, digestBuffer; MessageDigest md5; if (getState() != AUTHORIZATION_STATE) return false; md5 = MessageDigest.getInstance("MD5"); timestamp += secret; digest = md5.digest(timestamp.getBytes()); digestBuffer = new StringBuffer(128); for (i = 0; i < digest.length; i++) digestBuffer.append(Integer.toHexString(digest[i] & 0xff)); buffer = new StringBuffer(256); buffer.append(username); buffer.append(' '); buffer.append(digestBuffer.toString()); if (sendCommand(POP3Command.APOP, buffer.toString()) != POP3Reply.OK) return false; setState(TRANSACTION_STATE); return true; } /*** * Logout of the POP3 server. To fully disconnect from the server * you must call * {@link org.apache.commons.net.pop3.POP3#disconnect disconnect }. * A logout attempt is valid in any state. If * the client is in the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } * , it enters the * {@link org.apache.commons.net.pop3.POP3#UPDATE_STATE UPDATE_STATE } * on a successful logout. * <p> * @return True if the logout attempt was successful, false if not. * @exception IOException If a network I/O error occurs in the process * of logging out. ***/ public boolean logout() throws IOException { if (getState() == TRANSACTION_STATE) setState(UPDATE_STATE); sendCommand(POP3Command.QUIT); return (_replyCode == POP3Reply.OK); } /*** * Send a NOOP command to the POP3 server. This is useful for keeping * a connection alive since most POP3 servers will timeout after 10 * minutes of inactivity. A noop attempt will only succeed if * the client is in the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } * . * <p> * @return True if the noop attempt was successful, false if not. * @exception IOException If a network I/O error occurs in the process of * sending the NOOP command. ***/ public boolean noop() throws IOException { if (getState() == TRANSACTION_STATE) return (sendCommand(POP3Command.NOOP) == POP3Reply.OK); return false; } /*** * Delete a message from the POP3 server. The message is only marked * for deletion by the server. If you decide to unmark the message, you * must issuse a {@link #reset reset } command. Messages marked * for deletion are only deleted by the server on * {@link #logout logout }. * A delete attempt can only succeed if the client is in the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } * . * <p> * @param messageId The message number to delete. * @return True if the deletion attempt was successful, false if not. * @exception IOException If a network I/O error occurs in the process of * sending the delete command. ***/ public boolean deleteMessage(int messageId) throws IOException { if (getState() == TRANSACTION_STATE) return (sendCommand(POP3Command.DELE, Integer.toString(messageId)) == POP3Reply.OK); return false; } /*** * Reset the POP3 session. This is useful for undoing any message * deletions that may have been performed. A reset attempt can only * succeed if the client is in the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } * . * <p> * @return True if the reset attempt was successful, false if not. * @exception IOException If a network I/O error occurs in the process of * sending the reset command. ***/ public boolean reset() throws IOException { if (getState() == TRANSACTION_STATE) return (sendCommand(POP3Command.RSET) == POP3Reply.OK); return false; } /*** * Get the mailbox status. A status attempt can only * succeed if the client is in the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } * . Returns a POP3MessageInfo instance * containing the number of messages in the mailbox and the total * size of the messages in bytes. Returns null if the status the * attempt fails. * <p> * @return A POP3MessageInfo instance containing the number of * messages in the mailbox and the total size of the messages * in bytes. Returns null if the status the attempt fails. * @exception IOException If a network I/O error occurs in the process of * sending the status command. ***/ public POP3MessageInfo status() throws IOException { if (getState() != TRANSACTION_STATE) return null; if (sendCommand(POP3Command.STAT) != POP3Reply.OK) return null; return __parseStatus(_lastReplyLine.substring(3)); } /*** * List an individual message. A list attempt can only * succeed if the client is in the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } * . Returns a POP3MessageInfo instance * containing the number of the listed message and the * size of the message in bytes. Returns null if the list * attempt fails (e.g., if the specified message number does * not exist). * <p> * @param messageId The number of the message list. * @return A POP3MessageInfo instance containing the number of the * listed message and the size of the message in bytes. Returns * null if the list attempt fails. * @exception IOException If a network I/O error occurs in the process of * sending the list command. ***/ public POP3MessageInfo listMessage(int messageId) throws IOException { if (getState() != TRANSACTION_STATE) return null; if (sendCommand(POP3Command.LIST, Integer.toString(messageId)) != POP3Reply.OK) return null; return __parseStatus(_lastReplyLine.substring(3)); } /*** * List all messages. A list attempt can only * succeed if the client is in the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } * . Returns an array of POP3MessageInfo instances, * each containing the number of a message and its size in bytes. * If there are no messages, this method returns a zero length array. * If the list attempt fails, it returns null. * <p> * @return An array of POP3MessageInfo instances representing all messages * in the order they appear in the mailbox, * each containing the number of a message and its size in bytes. * If there are no messages, this method returns a zero length array. * If the list attempt fails, it returns null. * @exception IOException If a network I/O error occurs in the process of * sending the list command. ***/ public POP3MessageInfo[] listMessages() throws IOException { POP3MessageInfo[] messages; Enumeration<String> en; int line; if (getState() != TRANSACTION_STATE) return null; if (sendCommand(POP3Command.LIST) != POP3Reply.OK) return null; getAdditionalReply(); // This could be a zero length array if no messages present messages = new POP3MessageInfo[_replyLines.size() - 2]; en = _replyLines.elements(); // Skip first line en.nextElement(); // Fetch lines. for (line = 0; line < messages.length; line++) messages[line] = __parseStatus(en.nextElement()); return messages; } /*** * List the unique identifier for a message. A list attempt can only * succeed if the client is in the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } * . Returns a POP3MessageInfo instance * containing the number of the listed message and the * unique identifier for that message. Returns null if the list * attempt fails (e.g., if the specified message number does * not exist). * <p> * @param messageId The number of the message list. * @return A POP3MessageInfo instance containing the number of the * listed message and the unique identifier for that message. * Returns null if the list attempt fails. * @exception IOException If a network I/O error occurs in the process of * sending the list unique identifier command. ***/ public POP3MessageInfo listUniqueIdentifier(int messageId) throws IOException { if (getState() != TRANSACTION_STATE) return null; if (sendCommand(POP3Command.UIDL, Integer.toString(messageId)) != POP3Reply.OK) return null; return __parseUID(_lastReplyLine.substring(3)); } /*** * List the unique identifiers for all messages. A list attempt can only * succeed if the client is in the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } * . Returns an array of POP3MessageInfo instances, * each containing the number of a message and its unique identifier. * If there are no messages, this method returns a zero length array. * If the list attempt fails, it returns null. * <p> * @return An array of POP3MessageInfo instances representing all messages * in the order they appear in the mailbox, * each containing the number of a message and its unique identifier * If there are no messages, this method returns a zero length array. * If the list attempt fails, it returns null. * @exception IOException If a network I/O error occurs in the process of * sending the list unique identifier command. ***/ public POP3MessageInfo[] listUniqueIdentifiers() throws IOException { POP3MessageInfo[] messages; Enumeration<String> en; int line; if (getState() != TRANSACTION_STATE) return null; if (sendCommand(POP3Command.UIDL) != POP3Reply.OK) return null; getAdditionalReply(); // This could be a zero length array if no messages present messages = new POP3MessageInfo[_replyLines.size() - 2]; en = _replyLines.elements(); // Skip first line en.nextElement(); // Fetch lines. for (line = 0; line < messages.length; line++) messages[line] = __parseUID(en.nextElement()); return messages; } /*** * Retrieve a message from the POP3 server. A retrieve message attempt * can only succeed if the client is in the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } * . Returns a DotTerminatedMessageReader instance * from which the entire message can be read. * Returns null if the retrieval attempt fails (e.g., if the specified * message number does not exist). * <p> * You must not issue any commands to the POP3 server (i.e., call any * other methods) until you finish reading the message from the * returned Reader instance. * The POP3 protocol uses the same stream for issuing commands as it does * for returning results. Therefore the returned Reader actually reads * directly from the POP3 connection. After the end of message has been * reached, new commands can be executed and their replies read. If * you do not follow these requirements, your program will not work * properly. * <p> * @param messageId The number of the message to fetch. * @return A DotTerminatedMessageReader instance * from which the entire message can be read. * Returns null if the retrieval attempt fails (e.g., if the specified * message number does not exist). * @exception IOException If a network I/O error occurs in the process of * sending the retrieve message command. ***/ public Reader retrieveMessage(int messageId) throws IOException { if (getState() != TRANSACTION_STATE) return null; if (sendCommand(POP3Command.RETR, Integer.toString(messageId)) != POP3Reply.OK) return null; return new DotTerminatedMessageReader(_reader); } /*** * Retrieve only the specified top number of lines of a message from the * POP3 server. A retrieve top lines attempt * can only succeed if the client is in the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } * . Returns a DotTerminatedMessageReader instance * from which the specified top number of lines of the message can be * read. * Returns null if the retrieval attempt fails (e.g., if the specified * message number does not exist). * <p> * You must not issue any commands to the POP3 server (i.e., call any * other methods) until you finish reading the message from the returned * Reader instance. * The POP3 protocol uses the same stream for issuing commands as it does * for returning results. Therefore the returned Reader actually reads * directly from the POP3 connection. After the end of message has been * reached, new commands can be executed and their replies read. If * you do not follow these requirements, your program will not work * properly. * <p> * @param messageId The number of the message to fetch. * @param numLines The top number of lines to fetch. This must be >= 0. * @return A DotTerminatedMessageReader instance * from which the specified top number of lines of the message can be * read. * Returns null if the retrieval attempt fails (e.g., if the specified * message number does not exist). * @exception IOException If a network I/O error occurs in the process of * sending the top command. ***/ public Reader retrieveMessageTop(int messageId, int numLines) throws IOException { if (numLines < 0 || getState() != TRANSACTION_STATE) return null; if (sendCommand(POP3Command.TOP, Integer.toString(messageId) + " " + Integer.toString(numLines)) != POP3Reply.OK) return null; return new DotTerminatedMessageReader(_reader); } }